package com.atguigu.tingshu.common.login.aspect;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.constant.PublicConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.login.annotation.TingshuLogin;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @Author：yyj
 * @Package：com.atguigu.tingshu.common.login
 * @Project：tingshu_parent240829
 * @name：LoginAspect
 * @Date：2025/1/14 1:01
 * @Filename：LoginAspect
 */
@Component
@Aspect
public class LoginAspect {
    @Autowired
    private StringRedisTemplate redisTemplate;
    public static ThreadLocal<Long> longThreadLocal = new ThreadLocal<>();



    @Around(value = "@annotation(com.atguigu.tingshu.common.login.annotation.TingshuLogin)")
    public Object checkLogin(ProceedingJoinPoint pjp) throws Throwable {


        //拿到前端请求的token值

        //校验redis值token值和前端携带的是否相等

        //jwt验签和解密得到载荷进一步获取用户id

        //根据id通过user_info表查询用户信息

        //返回用户信息
        //前置通知
        //获取前端请求的token值
        String accessToken =getAccessTokenFromFront();
        //todo 判断required登录注解是否为true或者false
        Boolean isLogin=getLoginSwitch(pjp);//todo 是限制接口不是限制用户
        if (!isLogin){
            if (StringUtils.isEmpty(accessToken)){
                return pjp.proceed();//执行目标方法
            }
        }

        //校验accessToken和获取用户id
        Long userId=checkAccessTokenAndGetUserId(accessToken);
        AuthContextHolder.setUserId(userId);
        //longThreadLocal.set(userId);
        Object retVal;
        try {
            retVal = pjp.proceed();
        } finally {
            AuthContextHolder.removeUserId();//防止oom,把存放到ThreadLocal的key移除掉
        }
        //后置通知
        return retVal;
    }

    private boolean getLoginSwitch(ProceedingJoinPoint pjp) {
        //1.获取方法签名对象
        MethodSignature signature = (MethodSignature) pjp.getSignature();

        //2.获取方法对象
        Method method = signature.getMethod();

        //3.获取方法上的注解
        TingshuLogin annotation = method.getAnnotation(TingshuLogin.class);

        //4.获取注解的属性值
        boolean required = annotation.required();
        return required;
    }

    private Long checkAccessTokenAndGetUserId(String accessToken) {
        //1.校验前端是否携带accessToken
        if(StringUtils.isEmpty(accessToken)){
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        //2.进一步校验accessToken是否带对了
        //2.1.通过jwt验签判断,通过没有被篡改
        Jwt jwt = JwtHelper.decodeAndVerify(accessToken, new RsaVerifier(PublicConstant.PUBLIC_KEY));
        String claims = jwt.getClaims();
        Map map = JSONObject.parseObject(claims, Map.class);
        String openId = (String)map.get("openId");
        Object userId = map.get("userId");
        //2.2accessToken和服务端存储是否是同一个
        String accessTokenKey = RedisConstant.USER_LOGIN_KEY_PREFIX + openId;
        String fromRedisJwt = redisTemplate.opsForValue().get(accessTokenKey);
        if (StringUtils.isEmpty(fromRedisJwt)||!accessToken.equals(fromRedisJwt)){
            throw new GuiguException(401,"accessToken过期");
        }
        return Long.parseLong(userId+"");
    }

    private String getAccessTokenFromFront(){
        //获取请求对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取请求对象
        HttpServletRequest request = requestAttributes.getRequest();
        //获取请求头对象
        String refreshToken = request.getHeader("token");

        return refreshToken;
    }
}
